home *** CD-ROM | disk | FTP | other *** search
/ Java Programmer's Toolkit / Java Programmer's Toolkit.iso / applets / curve / curve.jav < prev    next >
Text File  |  1996-01-11  |  16KB  |  520 lines

  1. //**************************************************************************
  2. //*  Curve Applet by Michael Heinrichs
  3. //*
  4. //*  This applet allows the user to specify control points to be used for
  5. //*  plotting curves.  Three curve types are supported: Hermite, Bezier,
  6. //*  and B-Spline.  The user can add, delete and move control points.
  7. //*  
  8. //*  This applet was inspired by the DrawTest demo applet included in the
  9. //*  1.0 beta Java Developer's Kit from Sun Microsystems.
  10. //*  If you are interested in the algorithms used to display the curves,
  11. //*  Please see  the book: "Computer Graphics.  Principles and Practice" by
  12. //*  Foley, VanDam, Feiner, and Hughes.
  13. //**************************************************************************
  14.  
  15. import java.awt.*;
  16. import java.applet.*;
  17.  
  18. import java.util.Vector;
  19.  
  20. public class Curve extends Applet {
  21.     public void init() {
  22.     setLayout(new BorderLayout());
  23.     CurvePanel dp = new CurvePanel();
  24.     add("Center", dp);
  25.     add("South",new CurveControls(dp));
  26.     add("North",new CurveControls2(dp));
  27.     }
  28.  
  29.     public boolean handleEvent(Event e) {
  30.     switch (e.id) {
  31.       case Event.WINDOW_DESTROY:
  32.         System.exit(0);
  33.         return true;
  34.       default:
  35.         return false;
  36.     }
  37.     }
  38.  
  39.     public static void main(String args[]) {
  40.     Frame f = new Frame("Curve");
  41.     Curve curve = new Curve();
  42.     curve.init();
  43.     curve.start();
  44.  
  45.     f.add("Center", curve);
  46.     f.show();
  47.     }
  48. }
  49.  
  50. class ControlPoint extends Object {
  51.     public int x;
  52.     public int y;
  53.     public static final int PT_SIZE = 4;
  54.  
  55.     public ControlPoint(int a, int b) {
  56.     x = a;
  57.     y = b;
  58.     }
  59.  
  60.     public boolean within(int a, int b) {
  61.     if (a >= x - PT_SIZE && 
  62.         b >= y - PT_SIZE &&
  63.         a <= x + PT_SIZE && 
  64.         b <= y + PT_SIZE)
  65.         return true;
  66.     else
  67.         return false;
  68.     }
  69. }
  70.  
  71. class CurvePanel extends Panel {
  72.     public static final int HERMITE = 0;
  73.     public static final int BEZIER = 1;
  74.     public static final int BSPLINE = 2;
  75.     private int       mode = HERMITE;
  76.  
  77.     public static final int ADD = 0;
  78.     public static final int MOVE = 1;
  79.     public static final int DELETE = 2;
  80.     private int       action = ADD;
  81.  
  82.     private Vector points = new Vector(16,4);
  83.  
  84.     // If a control point is being moved, this is the index into the list
  85.     // of the moving point.  Otherwise it contains -1
  86.     private int moving_point;
  87.     private int precision;
  88.     private static float hermiteMatrix[][] = new float[4][4];
  89.     private static float bezierMatrix[][] = new float[4][4];
  90.     private static float bsplineMatrix[][] = new float[4][4];
  91.     private float eMatrix[][] = new float[4][4];
  92.  
  93.     // Initialize the curve-type matrices
  94.     static {
  95.     hermiteMatrix[0][0] = 2;
  96.     hermiteMatrix[0][1] = -2;
  97.     hermiteMatrix[0][2] = 1;
  98.     hermiteMatrix[0][3] = 1;
  99.     hermiteMatrix[1][0] = -3;
  100.     hermiteMatrix[1][1] = 3;
  101.     hermiteMatrix[1][2] = -2;
  102.     hermiteMatrix[1][3] = -1;
  103.     hermiteMatrix[2][0] = 0;
  104.     hermiteMatrix[2][1] = 0;
  105.     hermiteMatrix[2][2] = 1;
  106.     hermiteMatrix[2][3] = 0;
  107.     hermiteMatrix[3][0] = 1;
  108.     hermiteMatrix[3][1] = 0;
  109.     hermiteMatrix[3][2] = 0;
  110.     hermiteMatrix[3][3] = 0;
  111.  
  112.     bezierMatrix[0][0] = -1;
  113.     bezierMatrix[0][1] = 3;
  114.     bezierMatrix[0][2] = -3;
  115.     bezierMatrix[0][3] = 1;
  116.     bezierMatrix[1][0] = 3;
  117.     bezierMatrix[1][1] = -6;
  118.     bezierMatrix[1][2] = 3;
  119.     bezierMatrix[1][3] = 0;
  120.     bezierMatrix[2][0] = -3;
  121.     bezierMatrix[2][1] = 3;
  122.     bezierMatrix[2][2] = 0;
  123.     bezierMatrix[2][3] = 0;
  124.     bezierMatrix[3][0] = 1;
  125.     bezierMatrix[3][1] = 0;
  126.     bezierMatrix[3][2] = 0;
  127.     bezierMatrix[3][3] = 0;
  128.     
  129.     float mult = (float)(1.0/6.0);
  130.     bsplineMatrix[0][0] = -mult;
  131.     bsplineMatrix[0][1] = 3 * mult;
  132.     bsplineMatrix[0][2] = -3 * mult;
  133.     bsplineMatrix[0][3] = mult;
  134.     bsplineMatrix[1][0] = 3 * mult;
  135.     bsplineMatrix[1][1] = -6 * mult;
  136.     bsplineMatrix[1][2] = 3 * mult;
  137.     bsplineMatrix[1][3] = 0;
  138.     bsplineMatrix[2][0] = -3 * mult;
  139.     bsplineMatrix[2][1] = 0;
  140.     bsplineMatrix[2][2] = 3 * mult;
  141.     bsplineMatrix[2][3] = 0;
  142.     bsplineMatrix[3][0] = mult;
  143.     bsplineMatrix[3][1] = 4 * mult;
  144.     bsplineMatrix[3][2] = mult;
  145.     bsplineMatrix[3][3] = 0;
  146.     }
  147.  
  148.     public CurvePanel() {
  149.     setBackground(Color.white);
  150.     }
  151.  
  152.     private void calcEMatrix(int prec) {
  153.     // In order to use the "forward difference" method of curve plotting,
  154.     // we must generate this matrix.  The parameter indicates the precision;
  155.     // the number of line segments to use for each curve.
  156.  
  157.     float step = (float) (1.0/(float)prec);
  158.  
  159.     eMatrix[0][0] = 0;
  160.     eMatrix[0][1] = 0;
  161.     eMatrix[0][2] = 0;
  162.     eMatrix[0][3] = 1;
  163.     
  164.     eMatrix[1][2] = step;
  165.     eMatrix[1][1] = eMatrix[1][2] * step;
  166.     eMatrix[1][0] = eMatrix[1][1] * step;
  167.     eMatrix[1][3] = 0;
  168.  
  169.         eMatrix[2][0] = 6 * eMatrix[1][0]; 
  170.     eMatrix[2][1] = 2 * eMatrix[1][1];
  171.     eMatrix[2][2] = 0;
  172.     eMatrix[2][3] = 0;
  173.  
  174.     eMatrix[3][0] = eMatrix[2][0];
  175.     eMatrix[3][1] = 0;
  176.     eMatrix[3][2] = 0;
  177.     eMatrix[3][3] = 0;
  178.     }
  179.  
  180.     public void setAction(int action) {
  181.     // Change the action type
  182.     switch (action) {
  183.       case ADD:
  184.       case MOVE:
  185.       case DELETE:
  186.         this.action = action;
  187.         break;
  188.       default:
  189.         throw new IllegalArgumentException();
  190.     }
  191.     }
  192.  
  193.     public void setCurveType(int mode) {
  194.     // Change the curve display type
  195.     switch (mode) {
  196.       case HERMITE:
  197.       case BEZIER:
  198.       case BSPLINE:
  199.         this.mode = mode;
  200.         break;
  201.       default:
  202.         throw new IllegalArgumentException();
  203.     }
  204.     }
  205.  
  206.     public void setPrecision(int prec) {
  207.     precision = prec;
  208.     calcEMatrix(prec);
  209.     }
  210.  
  211.     public void clearPoints() {
  212.     points.removeAllElements();
  213.     }
  214.  
  215.     private int findPoint(int a, int b) {
  216.     // Scan the list of control points to find out which (if any) point
  217.     // contains the coordinates: a,b.
  218.     // If a point is found, return the point's index, otherwise return -1
  219.         int max = points.size();
  220.  
  221.     for(int i = 0; i < max; i++) {
  222.         ControlPoint pnt = (ControlPoint)points.elementAt(i);
  223.         if (pnt.within(a,b)) {
  224.         return i;
  225.         }
  226.     }
  227.     return -1;
  228.     }
  229.  
  230.     public boolean handleEvent(Event e) {
  231.     switch (e.id) {
  232.       case Event.MOUSE_DOWN:
  233.         // How we handle a MOUSE_DOWN depends on the action mode
  234.         switch (action) {
  235.           case ADD:
  236.         // Add a new control point at the specified location
  237.         ControlPoint pnt;
  238.         points.addElement(pnt = new ControlPoint(e.x, e.y));
  239.         repaint();
  240.         break;
  241.           case MOVE:
  242.         // Attempt to select the point at the location specified.
  243.         // If there is no point at the location, findPoint returns
  244.         // -1 (i.e. there is no point to be moved)
  245.         moving_point = findPoint(e.x, e.y);
  246.         break;
  247.           case DELETE:
  248.         // Delete a point if one has been clicked
  249.         int delete_pt = findPoint(e.x, e.y);
  250.         if(delete_pt >= 0) {
  251.            points.removeElementAt(delete_pt);
  252.            repaint();
  253.         }
  254.         break;
  255.           default:
  256.             throw new IllegalArgumentException();
  257.         }
  258.         return true;
  259.       case Event.MOUSE_UP:
  260.         // We only care about MOUSE_UP's if we've been moving a control
  261.         // point.  If so, drop the control point.
  262.         if (moving_point >=0) {
  263.         moving_point = -1;
  264.             repaint();
  265.         }
  266.         return true;
  267.       case Event.MOUSE_DRAG:
  268.         // We only care about MOUSE_DRAG's while we are moving a control
  269.         // point.  Otherwise, do nothing.
  270.         if (moving_point >=0) {
  271.         ControlPoint pnt = (ControlPoint) points.elementAt(moving_point);
  272.         pnt.x = e.x;
  273.         pnt.y = e.y;
  274.         repaint();
  275.         }
  276.         return true;
  277.       case Event.WINDOW_DESTROY:
  278.         System.exit(0);
  279.         return true;
  280.       default:
  281.         return false;
  282.     }
  283.     }
  284.  
  285.     private void multMatrix(float m[][], float g[][], float mg[][]) {
  286.     // This function performs the meat of the calculations for the
  287.     // curve plotting.  Note that it is not a matrix multiplier in the
  288.     // pure sense.  The first matrix is the curve matrix (each curve type
  289.     // has its own matrix), and the second matrix is the geometry matrix
  290.     // (defined by the control points).  The result is returned in the
  291.     // third matrix.
  292.  
  293.     // First clear the return array
  294.     for(int i=0; i<4; i++) 
  295.         for(int j=0; j<2; j++) 
  296.         mg[i][j]=0;
  297.  
  298.     // Perform the matrix math
  299.     for(int i=0; i<4; i++) 
  300.         for(int j=0; j<2; j++) 
  301.         for(int k=0; k<4; k++) 
  302.             mg[i][j]=mg[i][j] + (m[i][k] * g[k][j]);
  303.     }
  304.  
  305.     public void paint(Graphics g) {
  306.     int np = points.size();           // number of points
  307.         float geom[][] = new float[4][2]; // geometry matrix
  308.         float mg[][] = new float[4][2];   // 
  309.         float plot[][] = new float[4][2];
  310.  
  311.     g.setColor(getForeground());
  312.     g.setPaintMode();
  313.  
  314.     // draw a border around the canvas
  315.     g.drawRect(0,0, size().width-1, size().height-1);
  316.  
  317.     // draw the control points
  318.     for (int i=0; i < np; i++) {
  319.         ControlPoint p = (ControlPoint)points.elementAt(i);
  320.         g.drawRect(p.x-p.PT_SIZE, p.y-p.PT_SIZE, p.PT_SIZE*2, p.PT_SIZE*2);
  321.         g.drawString(String.valueOf(i),p.x+p.PT_SIZE,p.y-p.PT_SIZE);
  322.     }
  323.  
  324.     for(int i = 0; i < np-3;) {
  325.     // Four control points are needed to create a curve.
  326.     // If all the control points are used, the last series of four 
  327.     // points begins with point np-4.
  328.         switch (mode) {
  329.         // The geometry matrix for a series of control points is
  330.         // different for each curve type.
  331.             case(HERMITE):
  332.                 geom[0][0] = ((ControlPoint)points.elementAt(i)).x;
  333.                 geom[0][1] = ((ControlPoint)points.elementAt(i)).y;
  334.                 geom[1][0] = ((ControlPoint)points.elementAt(i+3)).x;
  335.                 geom[1][1] = ((ControlPoint)points.elementAt(i+3)).y;
  336.                 geom[2][0] = ((ControlPoint)points.elementAt(i+1)).x-geom[0][0];
  337.                 geom[2][1] = ((ControlPoint)points.elementAt(i+1)).y-geom[0][1];
  338.                 geom[3][0] = geom[1][0]-((ControlPoint)points.elementAt(i+2)).x;
  339.                 geom[3][1] = geom[1][1]-((ControlPoint)points.elementAt(i+2)).y;
  340.             multMatrix(hermiteMatrix, geom, mg);
  341.  
  342.             // The beginning of the next Hermite curve is the last
  343.             // point of the previous curve.
  344.             i += 3;
  345.             break;
  346.             case(BEZIER):
  347.             for(int j = 0; j <4 ;j++) {
  348.                     geom[j][0] = ((ControlPoint)points.elementAt(i+j)).x;
  349.                     geom[j][1] = ((ControlPoint)points.elementAt(i+j)).y;
  350.             }
  351.             multMatrix(bezierMatrix, geom, mg);
  352.  
  353.             // The beginning of the next Bezier curve is the last
  354.             // point of the previous curve.
  355.             i += 3;
  356.             break;
  357.             case(BSPLINE):
  358.             for(int j = 3; j >= 0; j--) {
  359.                     geom[3-j][0] = ((ControlPoint)points.elementAt(i+j)).x;
  360.                     geom[3-j][1] = ((ControlPoint)points.elementAt(i+j)).y;
  361.             }    
  362.             multMatrix(bsplineMatrix, geom, mg);
  363.  
  364.             // B-Spline is the slowest curve, since the beginning of
  365.             // the next series of four control points is the second
  366.             // control point of the previous series.
  367.             i++;
  368.             break;
  369.          }
  370.  
  371.          // In order to plot the curve using forward differences
  372.          // (a speedier way to plot the curve), another matrix
  373.          // calculation is required, taking into account the precision
  374.          // of the curve.
  375.          multMatrix(eMatrix, mg, plot);
  376.          float startX = plot[0][0];
  377.          float x = startX;
  378.          float startY = plot[0][1];
  379.          float y = startY;
  380.          // Plot the curve using the forward difference method
  381.          for(int j=0; j<precision; j++) {
  382.          x += plot[1][0];
  383.          plot[1][0] += plot[2][0];
  384.          plot[2][0] += plot[3][0];
  385.          y += plot[1][1];
  386.          plot[1][1] += plot[2][1];
  387.          plot[2][1] += plot[3][1];
  388.          g.drawLine((int)startX,(int)startY,(int)x,(int)y);
  389.          startX = x;
  390.          startY = y;
  391.          }
  392.     }
  393.     }
  394. }
  395.  
  396. class CurveControls extends Panel {
  397.     CurvePanel target;
  398.     Checkbox cb_add;
  399.     Checkbox cb_move;
  400.     Checkbox cb_delete;
  401.     String st_add_label = "Add Points";
  402.     String st_move_label = "Move Points";
  403.     String st_delete_label = "Delete Points";
  404.  
  405.     public CurveControls(CurvePanel target) {
  406.     this.target = target;
  407.     setLayout(new FlowLayout(FlowLayout.CENTER));
  408.     setBackground(Color.lightGray);
  409.     Button clear = new Button("Clear");
  410.     add("West",clear);
  411.  
  412.     CheckboxGroup action_group = new CheckboxGroup();
  413.     add(cb_add = new Checkbox(st_add_label, action_group, true));
  414.     add(cb_move = new Checkbox(st_move_label, action_group, false));
  415.     add(cb_delete = new Checkbox(st_delete_label, action_group, false));
  416.     }
  417.  
  418.     public void paint(Graphics g) {
  419.     Rectangle r = bounds();
  420.  
  421.     g.setColor(Color.lightGray);
  422.     g.draw3DRect(0, 0, r.width, r.height, false);
  423.     }
  424.  
  425.     public boolean action(Event e, Object arg) {
  426.     if (e.target instanceof Checkbox) {
  427.         String cbox = ((Checkbox)(e.target)).getLabel();
  428.         if (cbox.equals(st_add_label)) {
  429.             target.setAction(CurvePanel.ADD);
  430.         } else if (cbox.equals(st_move_label)) {
  431.             target.setAction(CurvePanel.MOVE);
  432.         } else if (cbox.equals(st_delete_label)) {
  433.             target.setAction(CurvePanel.DELETE);
  434.         }
  435.     } else if (e.target instanceof Button) {
  436.         String button = ((Button)(e.target)).getLabel();
  437.         if (button.equals("Clear")) {
  438.             target.clearPoints();
  439.  
  440.         // After clearing the control points, put the user back into
  441.         // ADD mode, since none of the other modes make any sense.
  442.         cb_add.setState(true);
  443.         cb_delete.setState(false);
  444.         cb_move.setState(false);
  445.             target.setAction(CurvePanel.ADD);
  446.         target.repaint();
  447.         }
  448.     }
  449.     return true;
  450.     }
  451. }
  452.  
  453. class CurveControls2 extends Panel {
  454.     CurvePanel target;
  455.     Checkbox cb_hermite;
  456.     Checkbox cb_bezier;
  457.     Checkbox cb_bspline;
  458.     String st_hermite_label = "Hermite";
  459.     String st_bezier_label = "Bezier";
  460.     String st_bspline_label = "B-Spline";
  461.     Scrollbar scroll;
  462.     Label precision_display;
  463.  
  464.     public CurveControls2(CurvePanel target) {
  465.     this.target = target;
  466.     setLayout(new FlowLayout(1));
  467.  
  468.     CheckboxGroup type_group = new CheckboxGroup();
  469.     add(cb_hermite = new Checkbox(st_hermite_label, type_group, true));
  470.     add(cb_bezier = new Checkbox(st_bezier_label, type_group, false));
  471.     add(cb_bspline = new Checkbox(st_bspline_label, type_group, false));
  472.  
  473.     // Set up the scrollbar: Value 15, Page 5, Min 2, Max 40
  474.     add(scroll = new Scrollbar(Scrollbar.HORIZONTAL, 15, 5, 2, 40));
  475.     target.setPrecision(scroll.getValue());
  476.     add(precision_display = new Label("Precision: "+
  477.         String.valueOf(scroll.getValue()),Label.LEFT));
  478.     }
  479.  
  480.     public void paint(Graphics g) {
  481.     Rectangle r = bounds();
  482.  
  483.     g.setColor(Color.lightGray);
  484.     g.draw3DRect(0, 0, r.width, r.height, false);
  485.     }
  486.  
  487.     public boolean handleEvent(Event e) {
  488.     switch (e.id) {
  489.       case Event.SCROLL_LINE_DOWN:
  490.       case Event.SCROLL_LINE_UP:
  491.       case Event.SCROLL_PAGE_DOWN:
  492.       case Event.SCROLL_PAGE_UP:
  493.       case Event.SCROLL_ABSOLUTE:
  494.       // For any of these events, get the precision value from the
  495.       // scrollbar, and update the curve precision, and the label.
  496.         target.setPrecision(((Scrollbar)e.target).getValue());
  497.         precision_display.setText("Precision: "+
  498.             String.valueOf(((Scrollbar)e.target).getValue()));
  499.         target.repaint();
  500.         return true;
  501.       case Event.ACTION_EVENT:
  502.       // Handle other action events
  503.         if (e.target instanceof Checkbox) {
  504.            String cbox = ((Checkbox)(e.target)).getLabel();
  505.            if (cbox.equals(st_hermite_label)) {
  506.               target.setCurveType(CurvePanel.HERMITE);
  507.            } else if (cbox.equals(st_bezier_label)) {
  508.               target.setCurveType(CurvePanel.BEZIER);
  509.            } else if (cbox.equals(st_bspline_label)) {
  510.               target.setCurveType(CurvePanel.BSPLINE);
  511.            }
  512.         }
  513.         target.repaint();
  514.         return(true);
  515.       default:
  516.         return(false);
  517.     }
  518.     }
  519. }
  520.